using System.Net;
using System.Text;
using SuperSocket.Channel;
using SuperSocket.ProtoBase;

namespace SuperSocket.Client.Proxy
{
    public class HttpConnector : ProxyConnectorBase<TextPackageInfo>
    {
        private const string ResponsePrefix = "HTTP/1.";
        private const char Space = ' ';

        public HttpConnector(EndPoint proxyEndPoint)
            : base(proxyEndPoint)
        {
        }

        public HttpConnector(EndPoint proxyEndPoint, IConnector nextConnector)
            : base(proxyEndPoint, nextConnector)
        {
        }

        public HttpConnector(EndPoint proxyEndPoint, string username, string password, IConnector nextConnector) : base(
            proxyEndPoint, username, password, nextConnector)
        {
        }

        protected override IChannel<TextPackageInfo> CreateChannel(ConnectState state, ChannelOptions channelOptions)
        {
            return state.CreateChannel(new LinePipelineFilter(Encoding.ASCII), channelOptions);
        }

        protected override async ValueTask<bool> ConnectProxyAsync(EndPoint remoteEndPoint,
            IChannel<TextPackageInfo> channel, IAsyncEnumerator<TextPackageInfo> packStream,
            CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            
            var request = new HttpRequest(channel, packStream, remoteEndPoint, Username, Password);

            var p = await request.SendHandshakeAsync();
            
            if (!HandleResponse(p, out var errorMessage))
                throw new Exception(errorMessage);

            return true;
        }

        private bool HandleResponse(TextPackageInfo p, out string message)
        {
            message = string.Empty;

            if (p == null)
                return false;

            var pos = p.Text.IndexOf(Space);

            // validating response
            if (!p.Text.StartsWith(ResponsePrefix, StringComparison.OrdinalIgnoreCase) || pos <= 0)
            {
                message = "Invalid response";
                return false;
            }

            if (!int.TryParse(p.Text.AsSpan().Slice(pos + 1, 3), out var statusCode))
            {
                message = "Invalid response";
                return false;
            }

            if (statusCode < 200 || statusCode > 299)
            {
                message = $"Invalid status code {statusCode}";
                return false;
            }

            return true;
        }
    }
}